Evolution of Server-Side JavaScript

Early Days

When we talk about server-side JavaScript, the first thing that comes to mind is Node.js. What you may not know is that backend JavaScript existed long before Node.

These are the ones that didn’t revolutionize the ecosystem. That didn’t inspire a community. The ones lost to the pages of history:

  • Jaxer
  • Silk
  • RingoJS
  • Rihno
  • AppEngineJS, etc.

JavaScript has been avoided for backend projects for a reason. Servers worked on the thread-per-request model, which didn’t play well with JavaScript that is single-threaded.

Having one thread occupied meant that servers would be slow as they could only process one request at a time.

To achieve multithreading, libraries like Ringo are built on top of Java Virtual Machine. However, even with JVM, none of them were able to solve the C10K problem (handling 10,000 concurrent connections on a single server).

There seemed to be no solution for this problem in JavaScript, that is until Ryan Dahl introduced Node.js in 2009.

Node.js wasn’t based on JVM, yet it solved the C10K problem.


So what made Node.js stand out? — It was non-blocking!

Node.js

Node.js is an open-source, cross-platform, asynchronous, event-driven JavaScript runtime environment that allows developers to run JavaScript code on the server. It’s built on the V8 JavaScript engine, which is the same engine that powers the Google Chrome browser.

Node.js allowed developers to work with the OS through JavaScript including:

  • File-System
  • Inspect Operating-System
  • Handle Processes

As well as make HTTP & TCP servers, use DNS, CLI and the list goes on.

Minimalism, speed, and modularity (achieved using CommonJS modules) made Node.js a perfect choice when developing microservices.

After all, Node.js is designed to build distributed applications with many nodes, thus the name Node.js.

But Node.js didn’t just raise the bar. It obliterated it and it all began with genius architecture running behind the scenes.

Non-Blocking I/O

Being built on Chrome’s V8 engine (C++) and Libuv (C library) allowed Node.js to process I/O and asynchronous tasks on the additional threads outside JavaScript, while the scheduling of the operations was done in the Event Loop.

JavaScript Event Loop draft from Asynchronous JavaScript blog

This means that Node.js can execute multiple API requests or reading/writing to a file/database without compromising on speed or in Layman’s terms — without blocking the main JavaScript thread.

This was often described as a waiter who can accept orders from new customers while other meals are cooked in the back, instead of accepting, waiting it to be cooked, and delivering one order at a time.

This formula is reused across Node.js for events, sockets, streams, and even hashing algorithms. Anything that is not a CPU-bound task.

The team behind Node.js went even further by allowing you to push your Node.js application to the limits of your CPU. By default, Node.js runs on a single process of your CPU and if it fails, well that’s it.

But by allowing developers to make use of child processes, Node.js can harness the full power of your machine by spawning a copy for each CPU and balancing traffic using a built-in load balancer.

And if one fails, Node.js forks another, and the cycle restarts.

Talk about going beyond a “single thread”.

Node.js delivers all this natively. Should you feel the need, you can enhance its abilities using Nginx or Process Managers like PM2, Docker, or Kubernetes.

The JavaScript runtimes that came after followed the footsteps of Node.js when designing their event-based (non-blocking) architecture.

But besides the native mechanics, Node.js also allowed upgrades in the form of third-party packages for utilities, libraries, and frameworks.

NPM

Node Package Manager (NPM), is a package manager for JavaScript language developed by Isaac Z. Schlueter. The purpose of NPM is to allow you to upgrade your Node.js or web app with a slew of third-party tools.

To add a package is as simple as this:

> npm i package-name

And that’s it.

The package is added to the project (as seen in the package.json file) and is ready to use. No need to look for CDNs yourself, nor set up .dlls.

Preview of Package.json file

Some packages are exclusive to server-side or web apps, while others work on both ends. And there is a variety of package managers:

  • Npm
  • Yarn

Since anyone can publish their own packages the NPM has grown into one of the largest registry of open-source tools.

Express.js

The standard HTTP server module that is built into Node.js didn’t meet the developer’s expectations. Soon came Express to save the day.

Express is a minimalistic API framework that popularized Node.js. The beauty of Express is that it takes only a few lines to start an HTTP server from scratch.

Another specialty of Express is that it is super extensible. What wasn’t developed by the core Express team, was developed by a community of open-source developers around the globe.

Full Stack JavaScript

Since Node.js is a JavaScript server technology, it means that the frontend developers who are already familiar with JavaScript can shift into backend developers without needing to learn a new programming language or a stack.

There was another newcomer on the block. Around the same year as Express, came out MongoDB as well, a document-based database that uses JSON structure to store data. The MongoDB team needed a fitting partner to popularize their database and they found one in Node.js & Express.

  • With a JavaScript web framework like (Angular.js)
  • Server-side JavaScript framework in the back (like Express.js)
  • Database that resembles JavaScript (MongoDB)
  • And a runtime environment built-in JavaScript (Node.js)

The JavaScript stacks were born.

Mongo Express Angular Node (MEAN) stack image by ZombieCode

ECMAScript 6

ES6 (sometimes referred to as ES2015) was a major JavaScript version that caused a turning point for the language.

  • Let & Const
  • Classes
  • Promises
  • ES Modules
  • Map and Set data structures
  • Arrow functions
  • Spread operator
  • Destructuring

Classes helped accommodate developers coming from other languages into JavaScript, even though for the most part, Classes are just a syntactic sugar for JavaScript Prototype inheritance.

Promises were a new way to manipulate asynchronous data, both simpler to use and faster than callbacks. ES6 also brought generator functions to the party.

New in ES6: Classes, Spread, array Destructuring

The callback syntax was much cleaner and simpler with Arrow functions, while the Spread operator and Destructuring made it easy to pull data out of complex structures.

The power of modules was brought to browsers as well. Unlike CommonJS modules that were used in Node.js, the new ES Modules imported packages asynchronously. This allowed a JavaScript script file to be split into smaller files connected together.

With changes made to the language, the server-side JavaScript (Node.js) had to adapt to it as well, like using ES6 features, allowing asynchronous operations to be handled via Promises (and later Async Await) as well as providing ES Modules support (the latter came out in Node.js v13).

Webpack explained by WebpackJS.org

Bundlers

There was one major drawback with all of these new language features and newer libraries — browser support. If something worked on Google Chrome, it may not behave the same or work at all on other web browsers. After all, every browser is built on a different JavaScript engine.

To combat this, the frameworks relied on Transpilers like Babel and Build tools such as Webpack to bundle the code written in ES6, React, or TypeScript into plain JavaScript.

These tools also provided various optimizations, like removing ES module imports (like library imports), converting ES6 (and future ES versions) code into ES5 equivalent, removing empty spaces, compressing images, as well as automating the process.

Once transpiled and bundled together, the code was fully executable in the browser & Node.js. Although tech has evolved over the years and all modern browsers support ES6, these techniques are still in use whenever the new ECMAScript version is released.

If you like my content and want to see more consider buying me a warm cup of coffee ☕

Buy me a coffee

Photo by Rafael Cruz from Medium

TypeScript

One of the biggest innovations (besides ES6) brought to the JavaScript language in recent years is TypeScript.

TypeScript is an open-source language that builds on JavaScript and extends it by adding strict types to the language. TypeScript restricts developers to using typed variables like string, number, boolean, and array but also introduces things like Dictionaries, Generics, Enums, Interfaces, Classes, and Tuples, and strict null checks, giving each functionality more context.